package cn.song.docflow.service.impl;

import cn.song.docflow.model.dto.ImageParamDTO;
import cn.song.docflow.model.dto.TemplateInformationDTO;
import cn.song.docflow.service.DocumentService;
import cn.song.docflow.util.DocumentUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.io.File;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;

/**
 * 文档服务实现类
 *
 * @author SongRenShuo
 * @date 2023/05/20
 */
@Slf4j
@Service
public class DocumentServiceImpl implements DocumentService {

    /**
     * 默认模板路径
     */
    @Value("${defaultTemplatePath:templates/defaultTemplate.docx}")
    private String defaultTemplatePath;

    /**
     * 模板目录
     */
    @Value("${file.template-dir:templates/}")
    private String templatesDir;

    /**
     * 文件导出目录
     */
    @Value("${file.export-dir:exports/}")
    private String exportDir;

    /**
     * 文件上传目录
     */
    @Value("${file.upload-dir:uploads/}")
    private String uploadDir;

    /**
     * 生成Word文档
     *
     * @param templateInfo 模板信息
     * @return 生成的Word文档路径
     * @throws IOException IO异常
     */
    @Override
    public String generateWord(TemplateInformationDTO templateInfo) throws IOException {
        // 获取模板路径
        String templatePath = getTemplatePath(templateInfo);

        // 转换DTO为数据映射
        Map<String, Object> dataMap = convertDtoToDataMap(templateInfo);

        // 生成Word文档
        String wordFilePath = DocumentUtil.generateWord(templatePath, dataMap, exportDir);
        log.info("生成Word文档成功: {}", wordFilePath);

        return wordFilePath;
    }

    /**
     * 生成PDF文档
     *
     * @param templateInfo 模板信息
     * @return 生成的PDF文档路径
     * @throws Exception 异常
     */
    @Override
    public String generatePdf(TemplateInformationDTO templateInfo) throws Exception {
        // 获取模板路径
        String templatePath = getTemplatePath(templateInfo);

        // 转换DTO为数据映射
        Map<String, Object> dataMap = convertDtoToDataMap(templateInfo);

        // 生成Word文档并转换为PDF
        String pdfFilePath;
        // 是否启用队列模式
        if (Boolean.TRUE.equals(templateInfo.getQueueMode())) {
            // 使用队列模式处理
            pdfFilePath = DocumentUtil.generateWordAndPdfQueue(templatePath, dataMap, exportDir);
        } else {
            // 直接处理模式
            pdfFilePath = DocumentUtil.generateWordAndPdf(templatePath, dataMap, exportDir);
        }
        log.info("生成PDF文档成功: {}", pdfFilePath);

        return pdfFilePath;
    }

    /**
     * 获取模板路径
     *
     * @param templateInfo 模板信息
     * @return 模板路径（确保返回绝对路径）
     */
    private String getTemplatePath(TemplateInformationDTO templateInfo) {
        log.info("开始处理模板路径，templateInfo: {}", templateInfo);
        log.info("当前配置: templatesDir={}, defaultTemplatePath={}", templatesDir, defaultTemplatePath);

        // 确保templatesDir末尾有分隔符
        String normalizedTemplatesDir = templatesDir.endsWith(File.separator) ? templatesDir : templatesDir + File.separator;
        log.info("标准化后的模板目录: {}", normalizedTemplatesDir);

        // 如果指定了模板路径，则使用指定的模板
        if (StringUtils.hasText(templateInfo.getTemplatePath())) {
            String templatePath = templateInfo.getTemplatePath();
            // 安全处理：不直接使用客户端传入的绝对路径，始终基于配置的模板目录构建路径
            // 1. 提取文件名部分
            String fileName = extractFileName(templatePath);
            // 2. 基于配置的模板目录构建完整路径
            String safeTemplatePath = normalizedTemplatesDir + fileName;
            File templateFile = new File(safeTemplatePath);
            String absolutePath = templateFile.getAbsolutePath();
            log.info("客户端传入模板路径: {}, 安全处理后路径: {}, 转换为绝对路径: {}, 文件是否存在: {}",
                    templatePath, safeTemplatePath, absolutePath, templateFile.exists());
            return absolutePath;
        }

        // 如果指定了模板代码，则根据模板代码查找对应的模板
        if (StringUtils.hasText(templateInfo.getTemplateCode())) {
            String templatePath = templatesDir + templateInfo.getTemplateCode() + ".docx";
            File templateFile = new File(templatePath);
            String absolutePath = templateFile.getAbsolutePath();
            log.info("根据模板代码构建的路径: {}, 转换为绝对路径: {}, 文件是否存在: {}",
                    templatePath, absolutePath, templateFile.exists());
            if (templateFile.exists()) {
                return absolutePath;
            }
            throw new RuntimeException("指定的模板代码 " + templateInfo.getTemplateCode() + " 对应的模板文件不存在: " + absolutePath);
        }

        // 否则使用默认模板
        log.info("使用默认模板路径: {}", defaultTemplatePath);
        File defaultTemplateFile = new File(defaultTemplatePath);
        String absolutePath = defaultTemplateFile.getAbsolutePath();
        log.info("默认模板转换为绝对路径: {}, 文件是否存在: {}", absolutePath, defaultTemplateFile.exists());

        if (!defaultTemplateFile.exists()) {
            log.error("默认模板文件不存在: {}", absolutePath);
        }

        return absolutePath;
    }

    /**
     * 安全地从文件路径中提取文件名
     * 避免使用客户端传入的绝对路径带来的安全隐患
     */
    private String extractFileName(String filePath) {
        if (filePath == null || filePath.isEmpty()) {
            throw new IllegalArgumentException("文件路径不能为空");
        }

        // 使用字符串操作安全地提取文件名，避免使用File对象带来的安全隐患
        int lastSeparatorIndex = filePath.lastIndexOf(File.separatorChar);
        int lastAltSeparatorIndex = filePath.lastIndexOf('/');

        // 取两种分隔符中最后出现的位置
        int lastIndex = Math.max(lastSeparatorIndex, lastAltSeparatorIndex);

        // 如果没有找到分隔符，则整个字符串就是文件名
        return (lastIndex == -1) ? filePath : filePath.substring(lastIndex + 1);
    }

    /**
     * 转换DTO为数据映射
     *
     * @param dto 模板信息DTO
     * @return 数据映射
     */
    private Map<String, Object> convertDtoToDataMap(TemplateInformationDTO dto) {
        Map<String, Object> dataMap = new HashMap<>();

        // 添加基本文档信息
        dataMap.put("documentId", dto.getDocumentId());
        dataMap.put("documentName", dto.getDocumentName());

        // 处理模板参数
        if (dto.getTemplateParams() != null) {
            dataMap.putAll(dto.getTemplateParams());
        }

        // 处理图片参数
        if (dto.getImageParams() == null) {
            return dataMap;
        }
        dto.getImageParams().forEach((key, imageParam) -> {
            if (imageParam == null || !StringUtils.hasText(imageParam.getPath())) {
                return;
            }
            // 处理图片路径，确保使用正确的绝对路径
            String imagePath = imageParam.getPath();
            // 只处理本地图片路径，不处理网络图片URL
            if (imagePath.startsWith("http://") || imagePath.startsWith("https://")) {
                // 网络图片直接使用
                dataMap.put(key, DocumentUtil.DataInfo.image(imageParam).getRenderData());
                return;
            }
            try {
                // 确保uploadDir末尾有分隔符
                String normalizedUploadDir = uploadDir.endsWith(File.separator) ? uploadDir : uploadDir + File.separator;

                String absoluteImagePath;
                // 检查imagePath是否已经包含uploadDir前缀或已经是绝对路径
                if (imagePath.startsWith("uploads/") || imagePath.startsWith("/uploads/") || Paths.get(imagePath).isAbsolute()) {
                    // 如果imagePath已经是绝对路径，直接使用
                    if (Paths.get(imagePath).isAbsolute()) {
                        absoluteImagePath = imagePath;
                    } else {
                        // 如果imagePath以uploads/或/uploads/开头，去掉前缀后构建绝对路径
                        int prefixLength = imagePath.startsWith("/uploads/") ? 9 : 7;
                        absoluteImagePath = Paths.get(normalizedUploadDir, imagePath.substring(prefixLength)).toFile().getAbsolutePath();
                    }
                } else {
                    // 否则构建绝对路径
                    File imageFile = Paths.get(normalizedUploadDir, imagePath).toFile();
                    absoluteImagePath = imageFile.getAbsolutePath();
                }

                log.info("处理图片路径: 原始路径={}, 转换为绝对路径={}", imagePath, absoluteImagePath);

                // 创建新的ImageParamDTO，使用绝对路径
                ImageParamDTO processedImageParam = new ImageParamDTO();
                processedImageParam.setPath(absoluteImagePath);
                processedImageParam.setWidth(imageParam.getWidth());
                processedImageParam.setHeight(imageParam.getHeight());

                dataMap.put(key, DocumentUtil.DataInfo.image(processedImageParam).getRenderData());
            } catch (Exception e) {
                log.error("处理图片路径失败: {}", e.getMessage());
                // 如果处理失败，仍然使用原始路径尝试
                dataMap.put(key, DocumentUtil.DataInfo.image(imageParam).getRenderData());
            }
        });

        return dataMap;
    }
}
